home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 3
/
Gold Medal Software - Volume 3 (Gold Medal) (1994).iso
/
graphics
/
3dvect30.arj
/
XMODE.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-11-18
|
116KB
|
3,335 lines
; ========================================================
; MODEX.ASM - A Complete Mode X Library
;
; Version 1.04 Release, 3 May 1993, By Matt Pritchard
; With considerable input from Michael Abrash
;
; The following information is donated to the public domain in
; the hopes that save other programmers much frustration.
;
; If you do use this code in a product, it would be nice if
; you include a line like "Mode X routines by Matt Pritchard"
; in the credits.
;
; Protected Mode modification by John McCarthy
; John McCarthy thanks Matt Pritchard for providing this code,
; and hopes Matt Pritchard is not offended by the changes.
;
; ========================================================
;
; All of this code is designed to be assembled with MASM 5.10a
; but TASM 3.0 could be used as well.
;
; The routines contained are designed for use in a MEDIUM model
; program. All Routines are FAR, and is assumed that a DGROUP
; data segment exists and that DS will point to it on entry.
;
; For all routines, the AX, BX, CX, DX, ES and FLAGS registers
; will not be preserved, while the DS, BP, SI and DI registers
; will be preserved.
;
; Unless specifically noted, All Parameters are assumed to be
; "PASSED BY VALUE". That is, the actual value is placed on
; the stack. When a reference is passed it is assumed to be
; a near pointer to a variable in the DGROUP segment.
;
; Routines that return a single 16-Bit integer value will
; return that value in the AX register.
;
; This code will *NOT* run on an 8086/8088 because 80286+
; specific instructions are used. If you have an 8088/86
; and VGA, you can buy an 80386-40 motherboard for about
; $160 and move into the 90's.
;
; JM - revision, this code will *NOT* run on 80286 because of
; 80368+ code is used (and protected mode).
;
; This code is reasonably optimized: Most drawing loops have
; been unrolled once and memory references are minimized by
; keeping stuff in registers when possible.
;
; Error Trapping varies by Routine. No Clipping is performed
; so the caller should verify that all coordinates are valid.
;
; Several Macros are used to simplify common 2 or 3 instruction
; sequences. Several Single letter Text Constants also
; simplify common assembler expressions like "WORD PTR".
;
; ------------------ Mode X Variations ------------------
;
; Mode # Screen Size Max Pages Aspect Ratio (X:Y)
;
; 0 320 x 200 4 Pages 1.2:1
; 1 320 x 400 2 Pages 2.4:1
; 2 360 x 200 3 Pages 1.35:1
; 3 360 x 400 1 Page 2.7:1
; 4 320 x 240 3 Pages 1:1
; 5 320 x 480 1 Page 2:1
; 6 360 x 240 3 Pages 1.125:1
; 7 360 x 480 1 Page 2.25:1
;
; -------------------- The Legal Stuff ------------------
;
; No warranty, either written or implied, is made as to
; the accuracy and usability of this code product. Use
; at your own risk. Batteries not included. Pepperoni
; and extra cheese available for an additional charge.
;
; ----------------------- The Author --------------------
;
; Matt Pritchard is a paid programmer who'd rather be
; writing games. He can be reached at: P.O. Box 140264,
; Irving, TX 75014 USA. Michael Abrash is a living
; god, who now works for Bill Gates (Microsoft).
;
; -------------------- Revision History -----------------
; 4-12-93: v1.02 - SET_POINT & READ_POINT now saves DI
; SET_MODEX now saves SI
; 5-3-93: v1.04 - added LOAD_DAC_REGISTERS and
; READ_DAC_REGISTERS. Expanded CLR Macro
; to handle multiple registers
;
; Revisions by John McCarthy - sometime throughout June/93:
;
; - Protected mode addressing for all routines.
; - Conditional assembley for routines not wanted by user
; - Bios text addressing removed - see set_vga_modex
; - File assembles for protected mode, variables and routines made public
; - Mode03 routine added to return to DOS mode. Must be used in conjunction
; with TRAN's protected mode header.
; - Also added routines to turn off the screen to avoid that screen flicker
; when changing into xmode.
;
; A note from John McCarthy:
;
; When converting to protected mode, loop's, rep's, stosb's and such
; must have the high word of the ecx,edi,esi registers set. I have tested
; the routines now that they have been converted and they seem to all work
; fine, but if you find they don't jive, please send me a message/letter and
; I will get right on it.
;
; These routines originally came with all kinds of extra files for font
; editing, pallet editing, C stuff and demos. I have only supplied the
; critical files from Matt to cut down on .zip file size.
;
x_fill_block equ 1 ; small routines are automatically included
x_draw_line equ 1 ; which x mode routines should be included
x_set_point equ 0 ; when assembled; 1 = enabled
x_read_point equ 0
x_gprintc equ 0
x_tgprintc equ 1
x_set_window equ 0
x_print_str equ 0
x_tprint_str equ 0
x_set_font equ 0
x_draw_bitmap equ 0
x_tdraw_bitmap equ 1
x_copy_page equ 0
x_copy_bitmap equ 0
.386p
jumps
code32 segment para public use32
assume cs:code32, ds:code32
include pmode.inc ; protected mode externals
include macros.inc ; guess...
include equ.inc ; list of constants
; xmode routines by matt prichard
; modified for 386 protected mode by john mccarthy
; protected mode header courtesy of TRAN
; ===== data tables =====
; bit mask tables for left/right/character masks
public left_clip_mask
public right_clip_mask
left_clip_mask db 0fh, 0eh, 0ch, 08h
right_clip_mask db 01h, 03h, 07h, 0fh
; bit patterns for converting character fonts
char_plane_data db 00h,08h,04h,0ch,02h,0ah,06h,0eh
db 01h,09h,05h,0dh,03h,0bh,07h,0fh
; crtc register values for various configurations
mode_single_line: ; crtc setup data for 400/480 line modes
dw 04009h ; cell height (1 scan line)
dw 00014h ; dword mode off
dw 0e317h ; turn on byte mode
dw nil ; end of crtc data for 400/480 line mode
mode_double_line: ; crtc setup data for 200/240 line modes
dw 04109h ; cell height (2 scan lines)
dw 00014h ; dword mode off
dw 0e317h ; turn on byte mode
dw nil ; end of crtc data for 200/240 line mode
mode_320_wide: ; crtc setup data for 320 horz pixels
dw 05f00h ; horz total
dw 04f01h ; horz displayed
dw 05002h ; start horz blanking
dw 08203h ; end horz blanking
dw 05404h ; start h sync
dw 08005h ; end h sync
dw nil ; end of crtc data for 320 horz pixels
mode_360_wide: ; crtc setup data for 360 horz pixels
dw 06b00h ; horz total
dw 05901h ; horz displayed
dw 05a02h ; start horz blanking
dw 08e03h ; end horz blanking
dw 05e04h ; start h sync
dw 08a05h ; end h sync
dw nil ; end of crtc data for 360 horz pixels
mode_200_tall:
mode_400_tall: ; crtc setup data for 200/400 line modes
dw 0bf06h ; vertical total
dw 01f07h ; overflow
dw 09c10h ; v sync start
dw 08e11h ; v sync end/prot cr0 cr7
dw 08f12h ; vertical displayed
dw 09615h ; v blank start
dw 0b916h ; v blank end
dw nil ; end of crtc data for 200/400 lines
mode_240_tall:
mode_480_tall: ; crtc setup data for 240/480 line modes
dw 00d06h ; vertical total
dw 03e07h ; overflow
dw 0ea10h ; v sync start
dw 08c11h ; v sync end/prot cr0 cr7
dw 0df12h ; vertical displayed
dw 0e715h ; v blank start
dw 00616h ; v blank end
dw nil ; end of crtc data for 240/480 lines
; table of display mode tables
mode_table:
dd o mode_320x200, o mode_320x400
dd o mode_360x200, o mode_360x400
dd o mode_320x240, o mode_320x480
dd o mode_360x240, o mode_360x480
; table of display mode components
mode_320x200: ; data for 320 by 200 pixels
db 063h ; 400 scan lines & 25 mhz clock
db 4 ; maximum of 4 pages
dw 320, 200 ; displayed pixels (x,y)
dw 1302, 816 ; max possible x and y sizes
dd o mode_320_wide, o mode_200_tall
dd o mode_double_line, nil
mode_320x400: ; data for 320 by 400 pixels
db 063h ; 400 scan lines & 25 mhz clock
db 2 ; maximum of 2 pages
dw 320, 400 ; displayed pixels x,y
dw 648, 816 ; max possible x and y sizes
dd o mode_320_wide, o mode_400_tall
dd o mode_single_line, nil
mode_360x240: ; data for 360 by 240 pixels
db 0e7h ; 480 scan lines & 28 mhz clock
db 3 ; maximum of 3 pages
dw 360, 240 ; displayed pixels x,y
dw 1092, 728 ; max possible x and y sizes
dd o mode_360_wide, o mode_240_tall
dd o mode_double_line , nil
mode_360x480: ; data for 360 by 480 pixels
db 0e7h ; 480 scan lines & 28 mhz clock
db 1 ; only 1 page possible
dw 360, 480 ; displayed pixels x,y
dw 544, 728 ; max possible x and y sizes
dd o mode_360_wide, o mode_480_tall
dd o mode_single_line , nil
mode_320x240: ; data for 320 by 240 pixels
db 0e3h ; 480 scan lines & 25 mhz clock
db 3 ; maximum of 3 pages
dw 320, 240 ; displayed pixels x,y
dw 1088, 818 ; max possible x and y sizes
dd o mode_320_wide, o mode_240_tall
dd o mode_double_line, nil
mode_320x480: ; data for 320 by 480 pixels
db 0e3h ; 480 scan lines & 25 mhz clock
db 1 ; only 1 page possible
dw 320, 480 ; displayed pixels x,y
dw 540, 818 ; max possible x and y sizes
dd o mode_320_wide, o mode_480_tall
dd o mode_single_line, nil
mode_360x200: ; data for 360 by 200 pixels
db 067h ; 400 scan lines & 28 mhz clock
db 3 ; maximum of 3 pages
dw 360, 200 ; displayed pixels (x,y)
dw 1302, 728 ; max possible x and y sizes
dd o mode_360_wide, o mode_200_tall
dd o mode_double_line, nil
mode_360x400: ; data for 360 by 400 pixels
db 067h ; 400 scan lines & 28 mhz clock
db 1 ; maximum of 1 pages
dw 360, 400 ; displayed pixels x,y
dw 648, 816 ; max possible x and y sizes
dd o mode_360_wide, o mode_400_tall
dd o mode_single_line, nil
mode_data_table struc
m_miscr db ? ; value of misc_output register
m_pages db ? ; maximum possible # of pages
m_xsize dw ? ; x size displayed on screen
m_ysize dw ? ; y size displayed on screen
m_xmax dw ? ; maximum possible x size
m_ymax dw ? ; maximum possible y size
m_crtc dd ? ; table of crtc register values
mode_data_table ends
; specific x mode data table format...
screen_width dw 0 ; actual width of a line in bytes
screen_height dw 0 ; actual vertical height in pixels
last_page dw 0 ; # of display pages
page_addr dd 4 dup (0) ; offsets to start of each page
page_size dw 0 ; size of page in addr bytes
display_page dw 0 ; page # currently displayed
active_page dw 0 ; page # currently active
current_page dd 0 ; address of current page
current_xoffset dw 0 ; current display x offset
current_yoffset dw 0 ; current display y offset
current_moffset dd 0 ; current start offset
max_xoffset dw 0 ; current display x offset
max_yoffset dw 0 ; current display y offset
charset_low dd 0 ; far ptr to char set: 0-127
charset_hi dd 0 ; far ptr to char set: 128-255
public screen_width
public screen_height
public last_page
public page_addr
public page_size
public display_page
public active_page
public current_page
public current_xoffset
public current_yoffset
public current_moffset
public max_xoffset
public max_yoffset
public charset_low
public charset_hi
; ===== mode x setup routines =====
;======================================================
;set_vga_modex% (modetype%, maxxpos%, maxypos%, pages%)
;======================================================
;
; sets up the specified version of mode x. allows for
; the setup of multiple video pages, and a virtual
; screen which can be larger than the displayed screen
; (which can then be scrolled a pixel at a time)
;
; entry: modetype = desired screen resolution (0-7)
;
; 0 = 320 x 200, 4 pages max, 1.2:1 aspect ratio
; 1 = 320 x 400, 2 pages max, 2.4:1 aspect ratio
; 2 = 360 x 200, 3 pages max, 1.35:1 aspect ratio
; 3 = 360 x 400, 1 page max, 2.7:1 aspect ratio
; 4 = 320 x 240, 3 pages max, 1:1 aspect ratio
; 5 = 320 x 480, 1 page max, 2:1 aspect ratio
; 6 = 360 x 240, 3 pages max, 1.125:1 aspect ratio
; 7 = 360 x 480, 1 page max, 2.25:1 aspect ratio
;
; maxxpos = the desired virtual screen width
; maxypos = the desired virtual screen height
; pages = the desired # of video pages
;
; exit: ax = success flag: 0 = failure / -1= success
;
svm_stack struc
svm_table dd ? ; offset of mode info table
dd ?x3 ; edi, esi, ebp
dd ? ; caller
svm_pages dw ? ; # of screen pages desired
svm_ysize dw ? ; vertical screen size desired
svm_xsize dw ? ; horizontal screen size desired
svm_mode dw ? ; display resolution desired
svm_stack ends
public set_vga_modex
set_vga_modex:
push ebp esi edi ; preserve important registers
sub esp, 4 ; allocate workspace
mov ebp, esp ; set up stack frame
; check legality of mode request....
movzx ebx, [ebp].svm_mode ; get requested mode #
cmp bx, num_modes ; is it 0..7?
jae @svm_badmodesetup ; if not, error out
shl bx, 2 ; scale bx
mov esi, d mode_table[ebx] ; si -> mode info
mov [ebp].svm_table, esi ; save ptr for later use
; check # of requested display pages
mov cx, [ebp].svm_pages ; get # of requested pages
clr ch ; set hi word = 0!
cmp cl, [esi].m_pages ; check # pages for mode
ja @svm_badmodesetup ; report error if too many pages
jcxz @svm_badmodesetup ; report error if 0 pages
; check validity of x size
and [ebp].svm_xsize, 0fff8h ; x size mod 8 must = 0
mov ax, [ebp].svm_xsize ; get logical screen width
cmp ax, [esi].m_xsize ; check against displayed x
jb @svm_badmodesetup ; report error if too small
cmp ax, [esi].m_xmax ; check against max x
ja @svm_badmodesetup ; report error if too big
; check validity of y size
mov bx, [ebp].svm_ysize ; get logical screen height
cmp bx, [esi].m_ysize ; check against displayed y
jb @svm_badmodesetup ; report error if too small
cmp bx, [esi].m_ymax ; check against max y
ja @svm_badmodesetup ; report error if too big
; enough memory to fit it all?
shr ax, 2 ; # of bytes:line = xsize/4
mul cx ; ax = bytes/line * pages
mul bx ; dx:ax = total vga mem needed
jno @svm_continue ; exit if total size > 256k
dec dx ; was it exactly 256k???
or dx, ax ; (dx = 1, ax = 0000)
jz @svm_continue ; if so, it's valid...
@svm_badmodesetup:
clr ax ; return value = false
jmp @svm_exit ; normal exit
@svm_continue:
mov v86r_ax,13h ; start with mode 13h
mov al,10h ; bios int 10h
int 33h ; let v86 handler call bios int 10h
call turn_screen_off ; prevent flicker
out_16 sc_index, chain4_off ; disable chain 4 mode
out_16 sc_index, async_reset ; (a)synchronous reset
out_8 misc_output, [esi].m_miscr ; set new timing/size
out_16 sc_index, sequ_restart ; restart sequencer ...
out_8 crtc_index, 11h ; select vert retrace end register
inc dx ; point to data
in al, dx ; get value, bit 7 = protect
and al, 7fh ; mask out write protect
out dx, al ; and send it back
mov dx, crtc_index ; vga crtc registers
add esi, m_crtc ; si -> crtc parameter data
; load tables of crtc parameters from list of tables
@svm_setup_table:
mov edi, [esi] ; get pointer to crtc data tbl
add esi, 4 ; point to next ptr entry
or edi, edi ; a nil ptr means that we have
jz @svm_set_data ; finished crtc programming
@svm_setup_crtc:
mov ax, [edi] ; get crtc data from table
add edi, 2 ; advance pointer
or ax, ax ; at end of data table?
jz @svm_setup_table ; if so, exit & get next table
out dx, ax ; reprogram vga crtc reg
jmp s @svm_setup_crtc ; process next table entry
; initialize page & scroll info, edi = 0
@svm_set_data:
mov display_page, di ; display page = 0
mov active_page, di ; active page = 0
mov current_xoffset, di ; horz scroll index = 0
mov current_yoffset, di ; vert scroll index = 0
mov current_moffset,edi ; memory scroll index = 0
@rlp esi, vga_segment ; segment for vga memory
mov current_page, esi
; set logical screen width, x scroll and our data
mov esi, [ebp].svm_table ; get saved ptr to mode info
mov ax, [ebp].svm_xsize ; get display width
mov cx, ax ; cx = logical width
sub cx, [esi].m_xsize ; cx = max x scroll value
mov max_xoffset, cx ; set maximum x scroll
shr ax, 2 ; bytes = pixels / 4
mov screen_width, ax ; save width in pixels
shr ax, 1 ; offset value = bytes / 2
mov ah, 13h ; crtc offset register index
xchg al, ah ; switch format for out
out dx, ax ; set vga crtc offset reg
; setup data table, y scroll, misc for other routines
mov ax, [ebp].svm_ysize ; get logical screen height
mov cx, ax ; cx = logical height
sub bx, [esi].m_ysize ; cx = max y scroll value
mov max_yoffset, cx ; set maximum y scroll
mov screen_height, ax ; save height in pixels
mul screen_width ; ax = page size in bytes,
mov page_size, ax ; save page size
mov cx, [ebp].svm_pages ; get # of pages
mov last_page, cx ; save # of pages
clr bx ; page # = 0
@rlp edx, vga_segment ; page 0 offset = 0a0000h
movzx eax,ax ; clear top word of eax
movzx ebx,bx
@svm_set_pages:
mov page_addr[ebx], edx ; set page #(bx) offset
add bx, 4 ; page#++
add edx, eax ; compute addr of next page
loopx cx, @svm_set_pages ; loop until all pages set
; clear vga memory
out_16 sc_index, all_planes_on ; select all planes
mov edi, current_page ; -> start of vga memory
clr ax ; ax = 0
cld ; block xfer forwards
mov ecx, 8000h ; 32k * 4 * 2 = 256k
rep stosw ; clear dat memory!
; setup font pointers, in protected mode. you must shl 8 then add but
; i don't know how to correctly convert segmented code to flat memory.
;
; mov v86r_bh, rom_8x8_lo ; ask for 8x8 font, 0-127
; mov v86r_ax, get_char_ptr ; service to get pointer
; mov al,10h
; int 33h ; call vga bios through v86 handler
;
; mov charset_low, bp ; save char set offset
; mov charset_low+2, es ; save char set segment
;
; mov v86r_bh, rom_8x8_hi ; ask for 8x8 font, 128-255
; mov v86r_ax, get_char_ptr ; service to get pointer
; mov al,10h
; int 33h ; call vga bios
;
; mov charset_hi, bp ; save char set offset
; mov charset_hi+2, es ; save char set segment
call turn_screen_on ; prevent flicker
mov ax, true ; return success code
@svm_exit:
add esp, 4 ; deallocate workspace
pop edi esi ebp ; restore saved registers
ret 8 ; exit & clean up stack
;==================
;set_modex% (mode%)
;==================
;
; quickie mode set - sets up mode x to default configuration
;
; entry: modetype = desired screen resolution (0-7)
; (see set_vga_modex for list)
;
; exit: ax = success flag: 0 = failure / -1= success
;
sm_stack struc
dd ?,? ; ebp, esi
dd ? ; caller
sm_mode dw ? ; desired screen resolution
sm_stack ends
public set_modex
set_modex:
push ebp esi ; preserve important registers
mov ebp, esp ; set up stack frame
clr ax ; assume failure
movzx ebx, [ebp].sm_mode ; get desired mode #
cmp bx, num_modes ; is it a valid mode #?
jae @smx_exit ; if not, don't bother
push bx ; push mode parameter
shl bx, 2 ; scale bx to dword indexer
mov esi, d mode_table[ebx] ; esi -> mode info
push [esi].m_xsize ; push default x size
push [esi].m_ysize ; push default y size
mov al, [esi].m_pages ; get default # of pages
clr ah ; hi byte = 0
push ax ; push # pages
call set_vga_modex ; set up mode x!
@smx_exit:
pop esi ebp ; restore registers
ret 2 ; exit & clean up stack
; ===== basic graphics primitives =====
;============================
;clear_vga_screen (colornum%)
;============================
;
; clears the active display page
;
; entry: colornum = color value to fill the page with
;
; exit: no meaningful values returned
;
cvs_stack struc
dd ?,? ; edi, ebp
dd ? ; caller
cvs_color db ?,? ; color to set screen to
cvs_stack ends
public clear_vga_screen
clear_vga_screen:
push ebp edi ; preserve important registers
mov ebp, esp ; set up stack frame
out_16 sc_index, all_planes_on ; select all planes
mov edi, current_page ; point to active vga page
mov al, [ebp].cvs_color ; get color
mov ah, al ; copy for dword write
mov bp,ax
shl eax,16
mov ax,bp
cld ; block fill forwards
movzx ecx, page_size ; get size of page
shr cx, 2 ; divide by 4 for dwords
rep stosd ; block fill vga memory
pop edi ebp ; restore saved registers
ret 2 ; exit & clean up stack
;===================================
;set_point (xpos%, ypos%, colornum%)
;===================================
;
; plots a single pixel on the active display page
;
; entry: xpos = x position to plot pixel at
; ypos = y position to plot pixel at
; colornum = color to plot pixel with
;
; exit: no meaningful values returned
;
sp_stack struc
dd ?,? ; ebp, edi
dd ? ; caller
setp_color db ?,? ; color of point to plot
setp_ypos dw ? ; y pos of point to plot
setp_xpos dw ? ; x pos of point to plot
sp_stack ends
public set_point
set_point:
push ebp edi ; preserve registers
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
mov ax, [ebp].setp_ypos ; get line # of pixel
mul screen_width ; get offset to start of line
clr ebx ; wipe high word
mov bx, [ebp].setp_xpos ; get xpos
mov cx, bx ; copy to extract plane # from
shr bx, 2 ; x offset (bytes) = xpos/4
add bx, ax ; offset = width*ypos + xpos/4
mov ax, map_mask_plane1 ; map mask & plane select register
and cl, plane_bits ; get plane bits
shl ah, cl ; get plane select value
out_16 sc_index, ax ; select plane
mov al,[ebp].setp_color ; get pixel color
mov [edi+ebx], al ; draw pixel
pop edi ebp ; restore saved registers
ret 6 ; exit and clean up stack
;==========================
;read_point% (xpos%, ypos%)
;==========================
;
; read the color of a pixel from the active display page
;
; entry: xpos = x position of pixel to read
; ypos = y position of pixel to read
;
; exit: ax = color of pixel at (xpos, ypos)
;
rp_stack struc
dd ?,? ; ebp, edi
dd ? ; caller
rp_ypos dw ? ; y pos of point to read
rp_xpos dw ? ; x pos of point to read
rp_stack ends
public read_point
read_point:
push ebp edi ; preserve registers
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
mov ax, [ebp].rp_ypos ; get line # of pixel
mul screen_width ; get offset to start of line
clr ebx ; wipe high word
mov bx, [ebp].rp_xpos ; get xpos
mov cx, bx
shr bx, 2 ; x offset (bytes) = xpos/4
add bx, ax ; offset = width*ypos + xpos/4
mov al, read_map ; gc read mask register
mov ah, cl ; get xpos
and ah, plane_bits ; & mask out plane #
out_16 gc_index, ax ; select plane to read in
clr eax ; clear return value hi byte
mov al, [edi+ebx] ; get color of pixel
pop edi ebp ; restore saved registers
ret 4 ; exit and clean up stack
if x_fill_block eq 1
;======================================================
;fill_block (xpos1%, ypos1%, xpos2%, ypos2%, colornum%)
;======================================================
;
; fills a rectangular block on the active display page
;
; entry: xpos1 = left x position of area to fill
; ypos1 = top y position of area to fill
; xpos2 = right x position of area to fill
; ypos2 = bottom y position of area to fill
; colornum = color to fill area with
;
; exit: no meaningful values returned
;
fb_stack struc
dd ?x3 ; edi, esi, ebp
dd ? ; caller
fb_color db ?,? ; fill color
fb_ypos2 dw ? ; y pos of lower right pixel
fb_xpos2 dw ? ; x pos of lower right pixel
fb_ypos1 dw ? ; y pos of upper left pixel
fb_xpos1 dw ? ; x pos of upper left pixel
fb_stack ends
public fill_block
fill_block:
push ebp esi edi ; preserve important registers
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
cld ; direction flag = forward
out_8 sc_index, map_mask ; set up for plane select
; validate pixel coordinates
; if necessary, swap so x1 <= x2, y1 <= y2
clr eax
clr ecx
mov ax, [ebp].fb_ypos1 ; ax = y1 is y1< y2?
mov bx, [ebp].fb_ypos2 ; bx = y2
cmp ax, bx
jle @fb_noswap1
mov [ebp].fb_ypos1, bx ; swap y1 and y2 and save y1
xchg ax, bx ; on stack for future use
@fb_noswap1:
sub bx, ax ; get y width
inc bx ; add 1 to avoid 0 value
mov [ebp].fb_ypos2, bx ; save in ypos2
mul screen_width ; mul y1 by bytes per line
add edi, eax ; di = start of line y1
mov ax, [ebp].fb_xpos1 ; check x1 <= x2
mov bx, [ebp].fb_xpos2 ;
cmp ax, bx
jle @fb_noswap2 ; skip ahead if ok
mov [ebp].fb_xpos2, ax ; swap x1 and x2 and save x2
xchg ax, bx ; on stack for future use
; all our input values are in order, now determine
; how many full "bands" 4 pixels wide (aligned) there
; are, and if there are partial bands (<4 pixels) on
; the left and right edges.
@fb_noswap2:
movzx edx, ax ; dx = x1 (pixel position)
shr edx, 2 ; dx/4 = bytes into line
add edi, edx ; di = addr of upper-left corner
movzx ecx, bx ; cx = x2 (pixel position)
shr cx, 2 ; cx/4 = bytes into line
cmp dx, cx ; start and end in same band?
jne @fb_normal ; if not, check for l & r edges
jmp @fb_one_band_only ; if so, then special processing
@fb_normal:
sub cx, dx ; cx = # bands -1
movzx esi, ax ; si = plane#(x1)
and si, plane_bits ; if left edge is aligned then
jz @fb_l_plane_flush ; no special processing..
; draw "left edge" vertical strip of 1-3 pixels...
out_8 sc_data, left_clip_mask[esi] ; set left edge plane mask
mov esi, edi ; si = copy of start addr (ul)
mov dx, [ebp].fb_ypos2 ; get # of lines to draw
mov al, [ebp].fb_color ; get fill color
movzx ebx, screen_width ; get vertical increment value
@fb_left_loop:
mov [esi], al ; fill in left edge pixels
add esi, ebx ; point to next line (below)
loopjz dx, @fb_left_cont ; exit loop if all lines drawn
mov [esi], al ; fill in left edge pixels
add esi, ebx ; point to next line (below)
loopx dx, @fb_left_loop ; loop until left strip is drawn
@fb_left_cont:
inc edi ; point to middle (or right) block
dec cx ; reset cx instead of jmp @fb_right
@fb_l_plane_flush:
inc cx ; add in left band to middle block
; di = addr of 1st middle pixel (band) to fill
; cx = # of bands to fill -1
@fb_right:
movzx esi, [ebp].fb_xpos2 ; get xpos2
and si, plane_bits ; get plane values
cmp si, 0003 ; plane = 3?
je @fb_r_edge_flush ; hey, add to middle
; draw "right edge" vertical strip of 1-3 pixels...
out_8 sc_data, right_clip_mask[esi] ; right edge plane mask
mov esi, edi ; get addr of left edge
add esi, ecx ; add width-1 (bands)
dec esi ; to point to top of right edge
mov dx, [ebp].fb_ypos2 ; get # of lines to draw
mov al, [ebp].fb_color ; get fill color
movzx ebx, screen_width ; get vertical increment value
@fb_right_loop:
mov [esi], al ; fill in right edge pixels
add esi, ebx ; point to next line (below)
loopjz dx, @fb_right_cont ; exit loop if all lines drawn
mov [esi], al ; fill in right edge pixels
add esi, ebx ; point to next line (below)
loopx dx, @fb_right_loop ; loop until left strip is drawn
@fb_right_cont:
dec cx ; minus 1 for middle bands
jz @fb_exit ; uh.. no middle bands...
@fb_r_edge_flush:
; di = addr of upper left block to fill
; cx = # of bands to fill in (width)
out_8 sc_data, all_planes ; write to all planes
mov dx, screen_width ; dx = di increment
sub dx, cx ; = screen_width-# planes filled
mov ebx, ecx ; bx = quick refill for cx
mov si, [ebp].fb_ypos2 ; si = # of line to fill
mov al, [ebp].fb_color ; get fill color
@fb_middle_loop:
rep stosb ; fill in entire line
mov ecx, ebx ; recharge cx (line width)
add edi, edx ; point to start of next line
loopx si, @fb_middle_loop ; loop until all lines drawn
jmp s @fb_exit ; outa here
@fb_one_band_only:
movzx esi, ax ; get left clip mask, save x1
and si, plane_bits ; mask out row #
mov al, left_clip_mask[esi] ; get left edge mask
mov si, bx ; get right clip mask, save x2
and si, plane_bits ; mask out row #
and al, right_clip_mask[esi] ; get right edge mask byte
out_8 sc_data, al ; clip for left & right masks
mov cx, [ebp].fb_ypos2 ; get # of lines to draw
mov al, [ebp].fb_color ; get fill color
clr ebx ; wipe high word
mov bx, screen_width ; get vertical increment value
@fb_one_loop:
mov [edi], al ; fill in pixels
add edi, ebx ; point to next line (below)
loopjz cx, @fb_exit ; exit loop if all lines drawn
mov [edi], al ; fill in pixels
add edi, ebx ; point to next line (below)
loopx cx, @fb_one_loop ; loop until left strip is drawn
@fb_exit:
pop edi esi ebp ; restore saved registers
ret 10 ; exit and clean up stack
endif
if x_draw_line eq 1
;=====================================================
;draw_line (xpos1%, ypos1%, xpos2%, ypos2%, colornum%)
;=====================================================
;
; draws a line on the active display page
;
; entry: xpos1 = x position of first point on line
; ypos1 = y position of first point on line
; xpos2 = x position of last point on line
; ypos2 = y position of last point on line
; colornum = color to draw line with
;
; exit: no meaningful values returned
;
dl_stack struc
dd ?x3 ; edi, esi, ebp
dd ? ; caller
dl_colorf db ?,? ; line draw color
dl_ypos2 dw ? ; y pos of last point
dl_xpos2 dw ? ; x pos of last point
dl_ypos1 dw ? ; y pos of first point
dl_xpos1 dw ? ; x pos of first point
dl_stack ends
public draw_line
draw_line:
push ebp esi edi ; preserve important registers
mov ebp, esp ; set up stack frame
cld ; direction flag = forward
out_8 sc_index, map_mask ; set up for plane select
mov ch, [ebp].dl_colorf ; save line color in ch
; check line type
movzx esi, [ebp].dl_xpos1 ; ax = x1 is x1< x2?
movzx edi, [ebp].dl_xpos2 ; dx = x2
cmp si, di ; is x1 < x2
je @dl_vline ; if x1=x2, draw vertical line
jl @dl_noswap1 ; if x1 < x2, don't swap
xchg si, di ; x2 is > x1, so swap them
@dl_noswap1:
; si = x1, di = x2
mov ax, [ebp].dl_ypos1 ; ax = y1 is y1 <> y2?
cmp ax, [ebp].dl_ypos2 ; y1 = y2?
je @dl_horz ; if so, draw a horizontal line
jmp @dl_brezham ; diagonal line... go do it...
; this code draws a horizontal line in mode x where:
; si = x1, di = x2, and ax = y1/y2
@dl_horz:
mul screen_width ; offset = ypos * screen_width
mov dx, ax ; cx = line offset into page
mov ax, si ; get left edge, save x1
and si, plane_bits ; mask out row #
mov bl, left_clip_mask[esi] ; get left edge mask
mov cx, di ; get right edge, save x2
and di, plane_bits ; mask out row #
mov bh, right_clip_mask[edi] ; get right edge mask byte
shr ax, 2 ; get x1 byte # (=x1/4)
shr cx, 2 ; get x2 byte # (=x2/4)
movzx eax, ax ; zero high words for add
movzx edx, dx
movzx ecx, cx
mov edi, current_page ; point to active vga page
add edi, edx ; point to start of line
add edi, eax ; point to pixel x1
sub cx, ax ; cx = # of bands (-1) to set
jnz @dl_longln ; jump if longer than one segment
and bl, bh ; otherwise, merge clip masks
@dl_longln:
out_8 sc_data, bl ; set the left clip mask
mov al, [ebp].dl_colorf ; get line color
mov bl, al ; bl = copy of line color
stosb ; set left (1-4) pixels
jcxz @dl_exit3 ; done if only one line segment
dec cx ; cx = # of middle segments
jz @dl_xrseg ; if no middle segments....
; draw middle segments
out_8 dx, all_planes ; write to all planes
mov al, bl ; get color from bl
rep stosb ; draw middle (4 pixel) segments
@dl_xrseg:
out_8 dx, bh ; select planes for right clip mask
mov al, bl ; get color value
stosb ; draw right (1-4) pixels
@dl_exit3:
jmp s @dl_exit ; we are done...
; this code draws a vertical line. on entry:
; ch = line color, si & di = x1
@dl_vline:
mov ax, [ebp].dl_ypos1 ; ax = y1
mov si, [ebp].dl_ypos2 ; si = y2
cmp ax, si ; is y1 < y2?
jle @dl_noswap2 ; if so, don't swap them
xchg ax, si ; ok, now y1 < y2
@dl_noswap2:
sub si, ax ; si = line height (y2-y1+1)
inc si
; ax = y1, di = x1, get offset into page into ax
mul screen_width ; offset = y1 (ax) * screen width
mov dx, di ; copy xpos into dx
shr di, 2 ; di = xpos/4
add ax, di ; di = xpos/4 + screenwidth * y1
movzx eax, ax
mov edi, current_page ; point to active vga page
add edi, eax ; point to pixel x1, y1
;select plane
mov cl, dl ; cl = save x1
and cl, plane_bits ; get x1 mod 4 (plane #)
mov ax, map_mask_plane1 ; code to set plane #1
shl ah, cl ; change to correct plane #
out_16 sc_index, ax ; select plane
mov al, ch ; get saved color
mov bx, screen_width ; get offset to advance line by
movzx ebx, bx
@dl_vloop:
mov [edi], al ; draw single pixel
add edi, ebx ; point to next line
loopjz si, @dl_exit ; lines--, exit if done
mov [edi], al ; draw single pixel
add edi, ebx ; point to next line
loopx si, @dl_vloop ; lines--, loop until done
@dl_exit:
jmp @dl_exit2 ; done!
; this code draws a diagonal line in mode x
@dl_brezham:
mov edi, current_page ; point to active vga page
mov ax, [ebp].dl_ypos1 ; get y1 value
mov bx, [ebp].dl_ypos2 ; get y2 value
mov cx, [ebp].dl_xpos1 ; get starting xpos
cmp bx, ax ; y2-y1 is?
jnc @dl_deltayok ; if y2>=y1 then goto...
xchg bx, ax ; swap em...
mov cx, [ebp].dl_xpos2 ; get new starting xpos
@dl_deltayok:
mul screen_width ; offset = screen_width * y1
movzx eax, ax
add edi, eax ; di -> start of line y1 on page
mov ax, cx ; ax = xpos (x1)
shr ax, 2 ; /4 = byte offset into line
add edi, eax ; di = starting pos (x1,y1)
mov al, 11h ; staring mask
and cl, plane_bits ; get plane #
shl al, cl ; and shift into place
mov ah, [ebp].dl_colorf ; color in hi bytes
push ax ; save mask,color...
mov ah, al ; plane # in ah
mov al, map_mask ; select plane register
out_16 sc_index, ax ; select initial plane
mov ax, [ebp].dl_xpos1 ; get x1 value
mov bx, [ebp].dl_ypos1 ; get y1 value
mov cx, [ebp].dl_xpos2 ; get x2 value
mov dx, [ebp].dl_ypos2 ; get y2 value
movzx ebp, screen_width ; use bp for line width to
; to avoid extra memory access
sub dx, bx ; figure delta_y
jnc @dl_deltayok2 ; jump if y2 >= y1
add bx, dx ; put y2 into y1
neg dx ; abs(delta_y)
xchg ax, cx ; and exchange x1 and x2
@dl_deltayok2:
mov bx, 08000h ; seed for fraction accumulator
sub cx, ax ; figure delta_x
jc @dl_drawleft ; if negative, go left
jmp @dl_drawright ; draw line that slopes right
@dl_drawleft:
neg cx ; abs(delta_x)
cmp cx, dx ; is delta_x < delta_y?
jb @dl_steepleft ; yes, so go do steep line
; (delta_y iterations)
; draw a shallow line to the left in mode x
@dl_shallowleft:
clr ax ; zero low word of delta_y * 10000h
sub ax, dx ; dx:ax <- dx * 0ffffh
sbb dx, 0 ; include carry
div cx ; divide by delta_x
mov si, bx ; si = accumulator
mov bx, ax ; bx = add fraction
pop ax ; get color, bit mask
mov dx, sc_data ; sequence controller data register
inc cx ; inc delta_x so we can unroll loop
; loop (x2) to draw pixels, move left, and maybe down...
@dl_sllloop:
mov [edi], ah ; set first pixel, plane data set up
loopjz cx, @dl_sllexit ; delta_x--, exit if done
add si, bx ; add numerator to accumulator
jnc @dl_slll2nc ; move down on carry
add edi, ebp ; move down one line...
@dl_slll2nc:
dec edi ; left one addr
ror al, 1 ; move left one plane, back on 0 1 2
cmp al, 87h ; wrap?, if al <88 then carry set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
mov [edi], ah ; set pixel
loopjz cx, @dl_sllexit ; delta_x--, exit if done
add si, bx ; add numerator to accumulator,
jnc @dl_slll3nc ; move down on carry
add edi, ebp ; move down one line...
@dl_slll3nc: ; now move left a pixel...
dec edi ; left one addr
ror al, 1 ; move left one plane, back on 0 1 2
cmp al, 87h ; wrap?, if al <88 then carry set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
jmp s @dl_sllloop ; loop until done
@dl_sllexit:
jmp @dl_exit2 ; and exit
; draw a steep line to the left in mode x
@dl_steepleft:
clr ax ; zero low word of delta_y * 10000h
xchg dx, cx ; delta_y switched with delta_x
div cx ; divide by delta_y
mov si, bx ; si = accumulator
mov bx, ax ; bx = add fraction
pop ax ; get color, bit mask
mov dx, sc_data ; sequence controller data register
inc cx ; inc delta_y so we can unroll loop
; loop (x2) to draw pixels, move down, and maybe left
@dl_stlloop:
mov [edi], ah ; set first pixel
loopjz cx, @dl_stlexit ; delta_y--, exit if done
add si, bx ; add numerator to accumulator
jnc @dl_stlnc2 ; no carry, just move down!
dec edi ; move left one addr
ror al, 1 ; move left one plane, back on 0 1 2
cmp al, 87h ; wrap?, if al <88 then carry set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
@dl_stlnc2:
add edi, ebp ; advance to next line.
mov [edi], ah ; set pixel
loopjz cx, @dl_stlexit ; delta_y--, exit if done
add si, bx ; add numerator to accumulator
jnc @dl_stlnc3 ; no carry, just move down!
dec edi ; move left one addr
ror al, 1 ; move left one plane, back on 0 1 2
cmp al, 87h ; wrap?, if al <88 then carry set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
@dl_stlnc3:
add edi, ebp ; advance to next line.
jmp s @dl_stlloop ; loop until done
@dl_stlexit:
jmp @dl_exit2 ; and exit
; draw a line that goes to the right...
@dl_drawright:
cmp cx, dx ; is delta_x < delta_y?
jb @dl_steepright ; yes, so go do steep line
; (delta_y iterations)
; draw a shallow line to the right in mode x
@dl_shallowright:
clr ax ; zero low word of delta_y * 10000h
sub ax, dx ; dx:ax <- dx * 0ffffh
sbb dx, 0 ; include carry
div cx ; divide by delta_x
mov si, bx ; si = accumulator
mov bx, ax ; bx = add fraction
pop ax ; get color, bit mask
mov dx, sc_data ; sequence controller data register
inc cx ; inc delta_x so we can unroll loop
; loop (x2) to draw pixels, move right, and maybe down...
@dl_slrloop:
mov [edi], ah ; set first pixel, mask is set up
loopjz cx, @dl_slrexit ; delta_x--, exit if done..
add si, bx ; add numerator to accumulator
jnc @dl_slr2nc ; don't move down if carry not set
add edi, ebp ; move down one line...
@dl_slr2nc: ; now move right a pixel...
rol al, 1 ; move right one addr if plane = 0
cmp al, 12h ; wrap? if al >12 then carry not set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
mov [edi], ah ; set pixel
loopjz cx, @dl_slrexit ; delta_x--, exit if done..
add si, bx ; add numerator to accumulator
jnc @dl_slr3nc ; don't move down if carry not set
add edi, ebp ; move down one line...
@dl_slr3nc:
rol al, 1 ; move right one addr if plane = 0
cmp al, 12h ; wrap? if al >12 then carry not set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
jmp s @dl_slrloop ; loop till done
@dl_slrexit:
jmp @dl_exit2 ; and exit
; draw a steep line to the right in mode x
@dl_steepright:
clr ax ; zero low word of delta_y * 10000h
xchg dx, cx ; delta_y switched with delta_x
div cx ; divide by delta_y
mov si, bx ; si = accumulator
mov bx, ax ; bx = add fraction
pop ax ; get color, bit mask
mov dx, sc_data ; sequence controller data register
inc cx ; inc delta_y so we can unroll loop
; loop (x2) to draw pixels, move down, and maybe right
@strloop:
mov [edi], ah ; set first pixel, mask is set up
loopjz cx, @dl_exit2 ; delta_y--, exit if done
add si, bx ; add numerator to accumulator
jnc @strnc2 ; if no carry then just go down...
rol al, 1 ; move right one addr if plane = 0
cmp al, 12h ; wrap? if al >12 then carry not set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
@strnc2:
add edi, ebp ; advance to next line.
mov [edi], ah ; set pixel
loopjz cx, @dl_exit2 ; delta_y--, exit if done
add si, bx ; add numerator to accumulator
jnc @strnc3 ; if no carry then just go down...
rol al, 1 ; move right one addr if plane = 0
cmp al, 12h ; wrap? if al >12 then carry not set
adc edi, 0 ; adjust address: di = di + carry
out dx, al ; set up new bit plane mask
@strnc3:
add edi, ebp ; advance to next line.
jmp s @strloop ; loop till done
@dl_exit2:
pop edi esi ebp ; restore saved registers
ret 10 ; exit and clean up stack
endif
; ===== dac color register routines =====
;=================================================
;set_dac_register (register%, red%, green%, blue%)
;=================================================
;
; sets a single (rgb) vga palette register
;
; entry: register = the dac # to modify (0-255)
; red = the new red intensity (0-63)
; green = the new green intensity (0-63)
; blue = the new blue intensity (0-63)
;
; exit: no meaningful values returned
;
sdr_stack struc
dd ? ; ebp
dd ? ; caller
sdr_blue db ?,? ; blue data value
sdr_green db ?,? ; green data value
sdr_red db ?,? ; red data value
sdr_register db ?,? ; palette register #
sdr_stack ends
public set_dac_register
set_dac_register:
push ebp ; save bp
mov ebp, esp ; set up stack frame
; select which dac register to modify
out_8 dac_write_addr, [ebp].sdr_register
mov dx, pel_data_reg ; dac data register
out_8 dx, [ebp].sdr_red ; set red intensity
out_8 dx, [ebp].sdr_green ; set green intensity
out_8 dx, [ebp].sdr_blue ; set blue intensity
pop ebp ; restore registers
ret 8 ; exit & clean up stack
;====================================================
;get_dac_register (register%, &red%, &green%, &blue%)
;====================================================
;
; reads the rgb values of a single vga palette register
;
; entry: register = the dac # to read (0-255)
; red = offset to red variable in ds
; green = offset to green variable in ds
; blue = offset to blue variable in ds
;
; exit: the values of the integer variables red,
; green, and blue are set to the values
; taken from the specified dac register.
;
gdr_stack struc
dd ? ; ebp
dd ? ; caller
gdr_blue dd ? ; addr of blue data value (where to put)
gdr_green dd ? ; addr of green data value
gdr_red dd ? ; addr of red data value
gdr_register db ?,? ; palette register #
gdr_stack ends
public get_dac_register
get_dac_register:
push ebp ; save bp
mov ebp, esp ; set up stack frame
; select which dac register to read in
out_8 dac_read_addr, [ebp].gdr_register
mov dx, pel_data_reg ; dac data register
clr ax ; clear ax
in al, dx ; read red value
mov ebx, [ebp].gdr_red ; get address of red%
mov [ebx], ax ; *red% = ax
in al, dx ; read green value
mov ebx, [ebp].gdr_green; get address of green%
mov [ebx], ax ; *green% = ax
in al, dx ; read blue value
mov ebx, [ebp].gdr_blue ; get address of blue%
mov [ebx], ax ; *blue% = ax
pop ebp ; restore registers
ret 14 ; exit & clean up stack
;===========================================================
;load_dac_registers (seg paldata, startreg%, endreg%, sync%)
;===========================================================
;
; sets a block of vga palette registers
;
; entry: paldata = far pointer to block of palette data
; startreg = first register # in range to set (0-255)
; endreg = last register # in range to set (0-255)
; sync = wait for vertical retrace flag (boolean)
;
; exit: no meaningful values returned
;
; notes: paldata is a lifar array of 3 byte palette values
; in the order: red (0-63), green (0-63), blue (0-63)
;
ldr_stack struc
dd ?,? ; ebp, esi
dd ? ; caller
ldr_sync dw ? ; vertical sync flag
ldr_endreg db ?,? ; last register #
ldr_startreg db ?,? ; first register #
ldr_paldata dd ? ; far ptr to palette data
ldr_stack ends
public load_dac_registers
load_dac_registers:
push ebp esi ; save registers
mov ebp, esp ; set up stack frame
mov ax, [ebp].ldr_sync ; get vertical sync flag
or ax, ax ; is sync flag = 0?
jz @ldr_load ; if so, skip call
call sync_display ; wait for vsync
; determine register #'s, size to copy, etc
@ldr_load:
mov esi, [ebp].ldr_paldata ; esi -> palette data
mov dx, dac_write_addr ; dac register # selector
clr ax, bx ; clear for byte loads
mov al, [ebp].ldr_startreg ; get start register
mov bl, [ebp].ldr_endreg ; get end register
sub bx, ax ; bx = # of dac registers -1
inc bx ; bx = # of dac registers
mov cx, bx ; cx = # of dac registers
add cx, bx ; cx = " " * 2
add cx, bx ; cx = " " * 3
cld ; block outs forward
out dx, al ; set up correct register #
; load a block of dac registers
mov dx, pel_data_reg ; dac data register
movzx ecx,cx
rep outsb ; block set dac registers
pop esi ebp ; restore registers
ret 10 ; exit & clean up stack
;====================================================
;read_dac_registers (seg paldata, startreg%, endreg%)
;====================================================
;
; reads a block of vga palette registers
;
; entry: paldata = far pointer to block to store palette data
; startreg = first register # in range to read (0-255)
; endreg = last register # in range to read (0-255)
;
; exit: no meaningful values returned
;
; notes: paldata is a lifar array of 3 byte palette values
; in the order: red (0-63), green (0-63), blue (0-63)
;
rdr_stack struc
dd ?,? ; ebp, edi
dd ? ; caller
rdr_endreg db ?,? ; last register #
rdr_startreg db ?,? ; first register #
rdr_paldata dd ? ; far ptr to palette data
rdr_stack ends
public read_dac_registers
read_dac_registers:
push ebp edi ; save registers
mov ebp, esp ; set up stack frame
; determine register #'s, size to copy, etc
mov edi, [ebp].rdr_paldata ; edi -> palette buffer
mov dx, dac_read_addr ; dac register # selector
clr ax, bx ; clear for byte loads
mov al, [ebp].rdr_startreg ; get start register
mov bl, [ebp].rdr_endreg ; get end register
sub bx, ax ; bx = # of dac registers -1
inc bx ; bx = # of dac registers
mov cx, bx ; cx = # of dac registers
add cx, bx ; cx = " " * 2
add cx, bx ; cx = " " * 3
cld ; block ins forward
; read a block of dac registers
out dx, al ; set up correct register #
mov dx, pel_data_reg ; dac data register
movzx ecx,cx
rep insb ; block read dac registers
pop edi ebp ; restore registers
ret 8 ; exit & clean up stack
; ===== page flipping and scrolling routines =====
;=========================
;set_active_page (pageno%)
;=========================
;
; sets the active display page to be used for future drawing
;
; entry: pageno = display page to make active
; (values: 0 to number of pages - 1)
;
; exit: no meaningful values returned
;
sap_stack struc
dd ? ; ebp
dd ? ; caller
sap_page dw ? ; page # for drawing
sap_stack ends
public set_active_page
set_active_page:
push ebp ; preserve registers
mov ebp, esp ; set up stack frame
movzx ebx, [ebp].sap_page ; get desired page #
cmp bx, last_page ; is page # valid?
jae @sap_exit ; if not, do nothing
mov active_page, bx ; set active page #
shl bx, 2 ; scale page # to dword
mov eax, page_addr[ebx] ; get offset to page
mov current_page, eax ; and set for future mov's
@sap_exit:
pop ebp ; restore registers
ret 2 ; exit and clean up stack
;================
;get_active_page%
;================
;
; returns the video page # currently used for drawing
;
; entry: no parameters are passed
;
; exit: ax = current video page used for drawing
;
public get_active_page
get_active_page:
mov ax, active_page ; get active page #
ret ; exit and clean up stack
;===============================
;set_display_page (displaypage%)
;===============================
;
; sets the currently visible display page.
; when called this routine syncronizes the display
; to the vertical blank.
;
; entry: pageno = display page to show on the screen
; (values: 0 to number of pages - 1)
;
; exit: no meaningful values returned
;
sdp_stack struc
dd ? ; ebp
dd ? ; caller
sdp_page dw ? ; page # to display...
sdp_stack ends
public set_display_page
set_display_page:
push ebp ; preserve registers
mov ebp, esp ; set up stack frame
movzx ebx, [ebp].sdp_page ; get desired page #
cmp bx, last_page ; is page # valid?
jae @sdp_exit ; if not, do nothing
mov display_page, bx ; set display page #
shl bx, 2 ; scale page # to dword
mov ecx, page_addr[ebx] ; get offset in memory to page
add ecx, current_moffset ; adjust for any scrolling
add ecx,_code32a ; adjust for protected mode
; wait if we are currently in a vertical retrace
mov dx, input_1 ; input status #1 register
@dp_wait0:
in al, dx ; get vga status
and al, vert_retrace ; in display mode yet?
jnz @dp_wait0 ; if not, wait for it
; set the start display address to the new page
mov dx, crtc_index ; we change the vga sequencer
mov al, start_disp_lo ; display start low register
mov ah, cl ; low 8 bits of start addr
out dx, ax ; set display addr low
mov al, start_disp_hi ; display start high register
mov ah, ch ; high 8 bits of start addr
out dx, ax ; set display addr high
; wait for a vertical retrace to smooth out things
mov dx, input_1 ; input status #1 register
@dp_wait1:
in al, dx ; get vga status
and al, vert_retrace ; vertical retrace start?
jz @dp_wait1 ; if not, wait for it
@sdp_exit:
pop ebp ; restore registers
ret 2 ; exit and clean up stack
;=================
;get_display_page%
;=================
;
; returns the video page # currently displayed
;
; entry: no parameters are passed
;
; exit: ax = current video page being displayed
;
public get_display_page
get_display_page:
mov ax, display_page ; get display page #
ret ; exit & clean up stack
if x_set_window eq 1
;=======================================
;set_window (displaypage%, xpos%, ypos%)
;=======================================
;
; since a logical screen can be larger than the physical
; screen, scrolling is possible. this routine sets the
; upper left corner of the screen to the specified pixel.
; also sets the display page to simplify combined page
; flipping and scrolling. when called this routine
; syncronizes the display to the vertical blank.
;
; entry: displaypage = display page to show on the screen
; xpos = # of pixels to shift screen right
; ypos = # of lines to shift screen down
;
; exit: no meaningful values returned
;
sw_stack struc
dd ? ; ebp
dd ? ; caller
sw_ypos dw ? ; y pos of ul screen corner
sw_xpos dw ? ; x pos of ul screen corner
sw_page dw ? ; (new) display page
sw_stack ends
public set_window
set_window:
push ebp ; preserve registers
mov ebp, esp ; set up stack frame
; check if our scroll offsets are valid
mov bx, [ebp].sw_page ; get desired page #
cmp bx, last_page ; is page # valid?
jae @sw_exit ; if not, do nothing
mov ax, [ebp].sw_ypos ; get desired y offset
cmp ax, max_yoffset ; is it within limits?
ja @sw_exit ; if not, exit
mov cx, [ebp].sw_xpos ; get desired x offset
cmp cx, max_xoffset ; is it within limits?
ja @sw_exit ; if not, exit
; compute proper display start address to use
mul screen_width ; ax = yoffset * line width
shr cx, 2 ; cx / 4 = bytes into line
add ax, cx ; ax = offset of upper left pixel
movzx eax, ax
movzx ebx, bx
mov current_moffset, eax ; save offset info
mov display_page, bx ; set current page #
shl bx, 2 ; scale page # to dword
add eax, page_addr[ebx] ; get offset in vga to page
add eax,_code32a ; adjust for protected mode segment
mov bx, ax ; bx = desired display start
mov dx, input_1 ; input status #1 register
; wait if we are currently in a vertical retrace
@sw_wait0:
in al, dx ; get vga status
and al, vert_retrace ; in display mode yet?
jnz @sw_wait0 ; if not, wait for it
; set the start display address to the new window
mov dx, crtc_index ; we change the vga sequencer
mov al, start_disp_lo ; display start low register
mov ah, bl ; low 8 bits of start addr
out dx, ax ; set display addr low
mov al, start_disp_hi ; display start high register
mov ah, bh ; high 8 bits of start addr
out dx, ax ; set display addr high
; wait for a vertical retrace to smooth out things
mov dx, input_1 ; input status #1 register
@sw_wait1:
in al, dx ; get vga status
and al, vert_retrace ; vertical retrace start?
jz @sw_wait1 ; if not, wait for it
; now set the horizontal pixel pan values
out_8 attrib_ctrl, pixel_pan_reg ; select pixel pan register
mov ax, [ebp].sw_xpos ; get desired x offset
and al, 03 ; get # of pixels to pan (0-3)
shl al, 1 ; shift for 256 color mode
out dx, al ; fine tune the display!
@sw_exit:
pop ebp ; restore saved registers
ret 6 ; exit and clean up stack
;=============
;get_x_offset%
;=============
;
; returns the x coordinate of the pixel currently display
; in the upper left corner of the display
;
; entry: no parameters are passed
;
; exit: ax = current horizontal scroll offset
;
public get_x_offset
get_x_offset:
mov ax, current_xoffset ; get current horz offset
ret ; exit & clean up stack
;=============
;get_y_offset%
;=============
;
; returns the y coordinate of the pixel currently display
; in the upper left corner of the display
;
; entry: no parameters are passed
;
; exit: ax = current vertical scroll offset
;
public get_y_offset
get_y_offset:
mov ax, current_yoffset ; get current vertical offset
ret ; exit & clean up stack
endif
;============
;sync_display
;============
;
; pauses the computer until the next vertical retrace starts
;
; entry: no parameters are passed
;
; exit: no meaningful values returned
;
public sync_display
sync_display:
mov dx, input_1 ; input status #1 register
; wait for any current retrace to end
@sd_wait0:
in al, dx ; get vga status
and al, vert_retrace ; in display mode yet?
jnz @sd_wait0 ; if not, wait for it
; wait for the start of the next vertical retrace
@sd_wait1:
in al, dx ; get vga status
and al, vert_retrace ; vertical retrace start?
jz @sd_wait1 ; if not, wait for it
ret
; ===== text display routines =====
if x_gprintc eq 1
;==================================================
;gprintc (charnum%, xpos%, ypos%, colorf%, colorb%)
;==================================================
;
; draws an ascii text character using the currently selected
; 8x8 font on the active display page. it would be a simple
; exercise to make this routine process variable height fonts.
;
; entry: charnum = ascii character # to draw
; xpos = x position to draw character at
; ypos = y position of to draw character at
; colorf = color to draw text character in
; colorb = color to set background to
;
; exit: no meaningful values returned
;
gpc_stack struc
gpc_width dd ? ; screen width-1
gpc_lines db ?,? ; scan lines to decode
gpc_t_sets dd ? ; saved charset segment
dd ?x3 ; edi, esi, ebp
dd ? ; caller
gpc_colorb db ?,? ; background color
gpc_colorf db ?,? ; text color
gpc_ypos dw ? ; y position to print at
gpc_xpos dw ? ; x position to print at
gpc_char db ?,? ; character to print
gpc_stack ends
public gprintc
gprintc:
push ebp esi edi ; preserve important registers
sub esp, 10 ; allocate workspace on stack
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
movzx eax, screen_width ; get logical line width
mov ebx, eax ; bx = screen width
dec bx ; = screen width-1
mov [ebp].gpc_width,ebx ; save for later use
mul [ebp].gpc_ypos ; start of line = ypos * width
add edi, eax ; di -> start of line ypos
movzx eax, [ebp].gpc_xpos ; get xpos of character
mov cx, ax ; save copy of xpos
shr ax, 2 ; bytes into line = xpos/4
add edi, eax ; di -> (xpos, ypos)
;get source addr of character bit map & save
mov al, [ebp].gpc_char ; get character #
test al, 080h ; is hi bit set?
jz @gpc_lowchar ; nope, use low char set ptr
mov ebx, charset_hi ; bx = char set ptr:offset
jmp s @gpc_set_char ; go setup character ptr
@gpc_lowchar:
mov ebx, charset_low ; bx = char set ptr:offset
@gpc_set_char:
and eax, 07fh ; mask out hi bits
shl ax, 3 ; * 8 bytes per bitmap
add ebx, eax ; bx = offset of selected char
mov [ebp].gpc_t_sets, ebx ; save segment on stack
and cx, plane_bits ; get plane #
mov ch, all_planes ; get initial plane mask
shl ch, cl ; and shift into position
and ch, all_planes ; and mask to lower nibble
mov al, 04 ; 4-plane # = # of initial
sub al, cl ; shifts to align bit mask
mov cl, al ; shift count for shl
;get segment of character map
out_8 sc_index, map_mask ; setup plane selections
inc dx ; dx -> sc_data
mov al, 08 ; 8 lines to process
mov [ebp].gpc_lines, al ; save on stack
@gpc_decode_char_byte:
mov esi, [ebp].gpc_t_sets ; get esi = string
mov bh, [esi] ; get bit map
inc esi ; point to next line
mov [ebp].gpc_t_sets, esi ; and save new pointer...
clr eax ; clear ax
clr bl ; clear bl
rol bx, cl ; bl holds left edge bits
movzx esi, bx ; use as table index
and si, char_bits ; get low bits
mov al, char_plane_data[esi] ; get mask in al
jz @gpc_no_left1bits ; skip if no pixels to set
mov ah, [ebp].gpc_colorf ; get foreground color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@gpc_no_left1bits:
xor al, ch ; invert mask for background
jz @gpc_no_left0bits ; hey, no need for this
mov ah, [ebp].gpc_colorb ; get background color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
;now do middle/last band
@gpc_no_left0bits:
inc edi ; point to next byte
rol bx, 4 ; shift 4 bits
movzx esi, bx ; make lookup pointer
and si, char_bits ; get low bits
mov al, char_plane_data[esi] ; get mask in al
jz @gpc_no_middle1bits ; skip if no pixels to set
mov ah, [ebp].gpc_colorf ; get foreground color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@gpc_no_middle1bits:
xor al, all_planes ; invert mask for background
jz @gpc_no_middle0bits ; hey, no need for this
mov ah, [ebp].gpc_colorb ; get background color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@gpc_no_middle0bits:
xor ch, all_planes ; invert clip mask
cmp cl, 4 ; aligned by 4?
jz @gpc_next_line ; if so, exit now..
inc edi ; point to next byte
rol bx, 4 ; shift 4 bits
movzx esi, bx ; make lookup pointer
and si, char_bits ; get low bits
mov al, char_plane_data[esi] ; get mask in al
jz @gpc_no_right1bits ; skip if no pixels to set
mov ah, [ebp].gpc_colorf ; get foreground color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@gpc_no_right1bits:
xor al, ch ; invert mask for background
jz @gpc_no_right0bits ; hey, no need for this
mov ah, [ebp].gpc_colorb ; get background color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@gpc_no_right0bits:
dec edi ; adjust for next line advance
@gpc_next_line:
add edi, [ebp].gpc_width ; point to next line
xor ch, char_bits ; flip the clip mask back
dec [ebp].gpc_lines ; count down lines
jz @gpc_exit ; ok... done!
jmp @gpc_decode_char_byte ; again! hey!
@gpc_exit:
add esp, 10 ; deallocate stack workspace
pop edi esi ebp ; restore saved registers
ret 10 ; exit and clean up stack
endif
if x_tgprintc eq 1
;==========================================
;tgprintc (charnum%, xpos%, ypos%, colorf%)
;==========================================
;
; transparently draws an ascii text character using the
; currently selected 8x8 font on the active display page.
;
; entry: charnum = ascii character # to draw
; xpos = x position to draw character at
; ypos = y position of to draw character at
; colorf = color to draw text character in
;
; exit: no meaningful values returned
;
tpc_stack struc
tpc_width dd ? ; screen width-1
tpc_lines db ?,? ; scan lines to decode
tpc_t_sets dd ? ; saved charset segment
dd ?x3 ; edi, esi, ebp
dd ? ; caller
tpc_colorf db ?,? ; text color
tpc_ypos dw ? ; y position to print at
tpc_xpos dw ? ; x position to print at
tpc_char db ?,? ; character to print
tpc_stack ends
public tgprintc
tgprintc:
push ebp esi edi ; preserve important registers
sub esp, 10 ; allocate workspace on stack
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
movzx eax, screen_width ; get logical line width
mov ebx, eax ; bx = screen width
dec bx ; = screen width-1
mov [ebp].tpc_width,ebx ; save for later use
mul [ebp].tpc_ypos ; start of line = ypos * width
add edi, eax ; di -> start of line ypos
movzx eax, [ebp].tpc_xpos ; get xpos of character
mov cx, ax ; save copy of xpos
shr ax, 2 ; bytes into line = xpos/4
add edi, eax ; di -> (xpos, ypos)
;get source addr of character bit map & save
mov al, [ebp].tpc_char ; get character #
test al, 080h ; is hi bit set?
jz @tpc_lowchar ; nope, use low char set ptr
mov ebx, charset_hi ; bx = char set ptr:offset
jmp s @tpc_set_char ; go setup character ptr
@tpc_lowchar:
mov ebx, charset_low ; bx = char set ptr:offset
@tpc_set_char:
and eax, 07fh ; mask out hi bits
shl ax, 3 ; * 8 bytes per bitmap
add ebx, eax ; bx = offset of selected char
mov [ebp].tpc_t_sets, ebx ; save segment on stack
and cx, plane_bits ; get plane #
mov ch, all_planes ; get initial plane mask
shl ch, cl ; and shift into position
and ch, all_planes ; and mask to lower nibble
mov al, 04 ; 4-plane # = # of initial
sub al, cl ; shifts to align bit mask
mov cl, al ; shift count for shl
;get segment of character map
out_8 sc_index, map_mask ; setup plane selections
inc dx ; dx -> sc_data
mov al, 08 ; 8 lines to process
mov [ebp].tpc_lines, al ; save on stack
@tpc_decode_char_byte:
mov esi, [ebp].tpc_t_sets ; get esi = string
mov bh, [esi] ; get bit map
inc esi ; point to next line
mov [ebp].tpc_t_sets, esi ; and save new pointer...
clr eax ; clear ax
clr bl ; clear bl
rol bx, cl ; bl holds left edge bits
movzx esi, bx ; use as table index
and si, char_bits ; get low bits
mov al, char_plane_data[esi] ; get mask in al
jz @tpc_no_left1bits ; skip if no pixels to set
mov ah, [ebp].tpc_colorf ; get foreground color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
;now do middle/last band
@tpc_no_left1bits:
inc edi ; point to next byte
rol bx, 4 ; shift 4 bits
movzx esi, bx ; make lookup pointer
and si, char_bits ; get low bits
mov al, char_plane_data[esi] ; get mask in al
jz @tpc_no_middle1bits ; skip if no pixels to set
mov ah, [ebp].tpc_colorf ; get foreground color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@tpc_no_middle1bits:
xor ch, all_planes ; invert clip mask
cmp cl, 4 ; aligned by 4?
jz @tpc_next_line ; if so, exit now..
inc edi ; point to next byte
rol bx, 4 ; shift 4 bits
movzx esi, bx ; make lookup pointer
and si, char_bits ; get low bits
mov al, char_plane_data[esi] ; get mask in al
jz @tpc_no_right1bits ; skip if no pixels to set
mov ah, [ebp].tpc_colorf ; get foreground color
out dx, al ; set up screen mask
mov [edi], ah ; write foreground color
@tpc_no_right1bits:
dec edi ; adjust for next line advance
@tpc_next_line:
add edi, [ebp].tpc_width ; point to next line
xor ch, char_bits ; flip the clip mask back
dec [ebp].tpc_lines ; count down lines
jz @tpc_exit ; ok... done!
jmp @tpc_decode_char_byte ; again! hey!
@tpc_exit:
add esp, 10 ; deallocate stack workspace
pop edi esi ebp ; restore saved registers
ret 8 ; exit and clean up stack
endif
if x_gprintc eq 1
;===============================================================
;print_str (seg string, maxlen%, xpos%, ypos%, colorf%, colorb%)
;===============================================================
;
; routine to quickly print a null terminated ascii string on the
; active display page up to a maximum length.
;
; entry: string = far pointer to ascii string to print
; maxlen = # of characters to print if no null found
; xpos = x position to draw text at
; ypos = y position of to draw text at
; colorf = color to draw text in
; colorb = color to set background to
;
; exit: no meaningful values returned
;
ps_stack struc
dd ?x3 ; edi, esi, ebp
dd ? ; caller
ps_colorb dw ? ; background color
ps_colorf dw ? ; text color
ps_ypos dw ? ; y position to print at
ps_xpos dw ? ; x position to print at
ps_len dw ? ; maximum length of string to print
ps_text dd ? ; far ptr to text string
ps_stack ends
public print_str
print_str:
push ebp esi edi ; preserve important registers
mov ebp, esp ; set up stack frame
@ps_print_it:
mov cx, [ebp].ps_len ; get remaining text length
jcxz @ps_exit ; exit when out of text
mov edi, [ebp].ps_text ; edi -> current char in text
mov al, [edi] ; al = text character
and ax, 00ffh ; clear high word
jz @ps_exit ; exit if null character
dec [ebp].ps_len ; remaining text length--
inc [ebp].ps_text ; point to next text char
; set up call to gprintc
push ax ; set character parameter
mov bx, [ebp].ps_xpos ; get xpos
push bx ; set xpos parameter
add bx, 8 ; advance 1 char to right
mov [ebp].ps_xpos, bx ; save for next time through
mov bx, [ebp].ps_ypos ; get ypos
push bx ; set ypos parameter
mov bx, [ebp].ps_colorf ; get text color
push bx ; set colorf parameter
mov bx, [ebp].ps_colorb ; get background color
push bx ; set colorb parameter
call gprintc ; print character!
jmp s @ps_print_it ; process next character
@ps_exit:
pop edi esi ebp ; restore saved registers
ret 14 ; exit and clean up stack
endif
if x_tgprintc eq 1
;================================================================
;tprint_str (seg string, maxlen%, xpos%, ypos%, colorf%, colorb%)
;================================================================
;
; routine to quickly transparently print a null terminated ascii
; string on the active display page up to a maximum length.
;
; entry: string = far pointer to ascii string to print
; maxlen = # of characters to print if no null found
; xpos = x position to draw text at
; ypos = y position of to draw text at
; colorf = color to draw text in
;
; exit: no meaningful values returned
;
tps_stack struc
dd ?x3 ; edi, esi, ebp
dd ? ; caller
tps_colorf dw ? ; text color
tps_ypos dw ? ; y position to print at
tps_xpos dw ? ; x position to print at
tps_len dw ? ; maximum length of string to print
tps_text dd ? ; far ptr to text string
tps_stack ends
public tprint_str
tprint_str:
push ebp esi edi ; preserve important registers
mov ebp, esp ; set up stack frame
@ts_print_it:
mov cx, [ebp].tps_len ; get remaining text length
jcxz @ts_exit ; exit when out of text
mov edi, [ebp].tps_text ; edi -> current char in text
mov al, [edi] ; al = text character
and ax, 00ffh ; clear high word
jz @ts_exit ; exit if null character
dec [ebp].tps_len ; remaining text length--
inc [ebp].tps_text ; point to next text char
; set up call to tgprintc
push ax ; set character parameter
mov bx, [ebp].tps_xpos ; get xpos
push bx ; set xpos parameter
add bx, 8 ; advance 1 char to right
mov [ebp].tps_xpos, bx ; save for next time through
mov bx, [ebp].tps_ypos ; get ypos
push bx ; set ypos parameter
mov bx, [ebp].tps_colorf ; get text color
push bx ; set colorf parameter
call tgprintc ; print character!
jmp s @ts_print_it ; process next character
@ts_exit:
pop edi esi ebp ; restore saved registers
ret 12 ; exit and clean up stack
endif
;===========================================
;set_display_font(seg fontdata, fontnumber%)
;===========================================
;
; allows the user to specify their own font data for
; wither the lower or upper 128 characters.
;
; entry: fontdata = far pointer to font bitmaps
; fontnumber = which half of set this is
; = 0, lower 128 characters
; = 1, upper 128 characters
;
; exit: no meaningful values returned
;
sdf_stack struc
dd ? ; ebp
dd ? ; caller
sdf_which dw ? ; hi table/low table flag
sdf_font dd ? ; far ptr to font table
sdf_stack ends
public set_display_font
set_display_font:
push ebp ; preserve registers
mov ebp, esp ; set up stack frame
mov edi, [ebp].sdf_font ; get far ptr to font
mov esi, o charset_low ; assume lower 128 chars
test [ebp].sdf_which, 1 ; font #1 selected?
jz @sdf_set_font ; if not, skip ahead
mov esi, o charset_hi ; ah, really it's 128-255
@sdf_set_font:
mov [esi], edi ; set font pointer offset
pop ebp ; restore registers
ret 6 ; we are done.. outa here
; ===== bitmap (sprite) display routines =====
;======================================================
;draw_bitmap (seg image, xpos%, ypos% )
;======================================================
;
; draws a variable sized graphics bitmap such as a
; picture or an icon on the current display page in
; mode x. the bitmap is stored in a lifar byte array
; corresponding to (0,0) (1,0), (2,0) .. (width, height)
; this is the same lifar manner as mode 13h graphics.
;
; entry: image = far pointer to bitmap data
; xpos = x position to place upper left pixel at
; ypos = y position to place upper left pixel at
; width = width of the bitmap in pixels - ommitted
; height = height of the bitmap in pixels - ommitted
;
; exit: no meaningful values returned
;
; routine has been modified so that first two words of bitmap define
; bitmap x and y size
;
db_stack struc
db_lineo dw ? ; offset to next line
db_pixcount dw ? ; (minimum) # of pixels/line
db_start dd ? ; addr of upper left pixel
db_pixskew dw ? ; # of bytes to adjust eol
db_skewflag dw ? ; extra pix on plane flag
db_height dw ? ; height of bitmap in pixels
dd ?x3 ; edi, esi, ebp
dd ? ; caller
db_ypos dw ? ; y position to draw bitmap at
db_xpos dw ? ; x position to draw bitmap at
db_image dd ? ; far pointer to graphics bitmap
db_stack ends
public draw_bitmap
draw_bitmap:
push ebp esi edi ; preserve important registers
sub esp, 14 ; allocate workspace
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
cld ; direction flag = forward
movzx eax, [ebp].db_ypos ; get ul corner ypos
mul screen_width ; ax = offset to line ypos
movzx ebx, [ebp].db_xpos ; get ul corner xpos
mov cl, bl ; save plane # in cl
shr bx, 2 ; xpos/4 = offset into line
add edi, eax ; edi -> start of line
add edi, ebx ; edi -> upper left pixel
mov [ebp].db_start, edi ; save starting addr
; compute line to line offset
mov esi, [ebp].db_image ; esi-> source image
lodsw ; get x width
mov bx,ax
lodsw
mov [ebp].db_height,ax
add [ebp].db_image,4
mov dx, bx ; save copy in dx
shr bx, 2 ; /4 = width in bands
mov ax, screen_width ; get screen width
sub ax, bx ; - (bitmap width/4)
mov [ebp].db_lineo, ax ; save line width offset
mov [ebp].db_pixcount, bx ; minimum # pix to copy
and dx, plane_bits ; get "partial band" size (0-3)
mov [ebp].db_pixskew, dx ; also end of line skew
mov [ebp].db_skewflag, dx ; save as flag/count
and cx, plane_bits ; cl = starting plane #
mov ax, map_mask_plane2 ; plane mask & plane select
shl ah, cl ; select correct plane
out_16 sc_index, ax ; select plane...
mov bh, ah ; bh = saved plane mask
mov bl, 4 ; bl = planes to copy
@db_copy_plane:
mov esi, [ebp].db_image ; esi-> source image
mov dx, [ebp].db_height ; # of lines to copy
mov edi, [ebp].db_start ; edi-> dest pos
@db_copy_line:
mov cx, [ebp].db_pixcount ; min # to copy
test cl, 0fch ; 16+pixwide?
jz @db_copy_remainder ; nope...
; pixel copy loop has been unrolled to x4
@db_copy_loop:
movsb ; copy bitmap pixel
add esi, 3 ; skip to next byte in same plane
movsb ; copy bitmap pixel
add esi, 3 ; skip to next byte in same plane
movsb ; copy bitmap pixel
add esi, 3 ; skip to next byte in same plane
movsb ; copy bitmap pixel
add esi, 3 ; skip to next byte in same plane
sub cl, 4 ; pixels to copy=-4
test cl, 0fch ; 4+ pixels left?
jnz @db_copy_loop ; if so, do another block
@db_copy_remainder:
jcxz @db_next_line ; any pixels left on line
@db_copy2:
movsb ; copy bitmap pixel
add esi,3 ; skip to next byte in same plane
loopx cx, @db_copy2 ; pixels to copy--, loop until done
@db_next_line:
; any partial pixels? (some planes only)
or cx, [ebp].db_skewflag ; get skew count
jz @db_next2 ; if no partial pixels
movsb ; copy bitmap pixel
dec edi ; back up to align
dec esi ; back up to align
@db_next2:
movzx eax, [ebp].db_pixskew ; adjust skew
add esi, eax
movzx eax, [ebp].db_lineo ; set to next display line
add edi, eax
loopx dx, @db_copy_line ; lines to copy--, loop if more
; copy next plane....
dec bl ; planes to go--
jz @db_exit ; hey! we are done
rol bh, 1 ; next plane in line...
out_8 sc_data, bh ; select plane
cmp al, 12h ; carry set if al=11h
adc [ebp].db_start, 0 ; screen addr =+carry
inc w [ebp].db_image ; start @ next byte
sub [ebp].db_skewflag, 1 ; reduce planes to skew
adc [ebp].db_skewflag, 0 ; back to 0 if it was -1
jmp f @db_copy_plane ; go copy the next plane
@db_exit:
add esp, 14 ; deallocate workspace
pop edi esi ebp ; restore saved registers
ret 8 ; exit and clean up stack
;=======================================================
;tdraw_bitmap (seg image, xpos%, ypos%)
;=======================================================
;
; transparently draws a variable sized graphics bitmap
; such as a picture or an icon on the current display page
; in mode x. pixels with a value of 0 are not drawn,
; leaving the previous "background" contents intact.
;
; the bitmap format is the same as for the draw_bitmap function.
;
; entry: image = far pointer to bitmap data
; xpos = x position to place upper left pixel at
; ypos = y position to place upper left pixel at
; width = width of the bitmap in pixels - ommitted
; height = height of the bitmap in pixels - ommitted
;
; exit: no meaningful values returned
;
; routine has been modified so that first two words of bitmap define
; bitmap x and y size
;
tb_stack struc
tb_lineo dw ? ; offset to next line
tb_pixcount dw ? ; (minimum) # of pixels/line
tb_start dd ? ; addr of upper left pixel
tb_pixskew dw ? ; # of bytes to adjust eol
tb_skewflag dw ? ; extra pix on plane flag
tb_height dw ? ; height of bitmap in pixels
dd ?x3 ; edi, esi, ebp
dd ? ; caller
tb_ypos dw ? ; y position to draw bitmap at
tb_xpos dw ? ; x position to draw bitmap at
tb_image dd ? ; far pointer to graphics bitmap
tb_stack ends
public tdraw_bitmap
tdraw_bitmap:
push ebp esi edi ; preserve important registers
sub esp, 14 ; allocate workspace
mov ebp, esp ; set up stack frame
mov edi, current_page ; point to active vga page
cld ; direction flag = forward
movzx eax, [ebp].tb_ypos ; get ul corner ypos
mul screen_width ; ax = offset to line ypos
movzx ebx, [ebp].tb_xpos ; get ul corner xpos
mov cl, bl ; save plane # in cl
shr bx, 2 ; xpos/4 = offset into line
add edi, eax ; edi -> start of line
add edi, ebx ; edi -> upper left pixel
mov [ebp].tb_start, edi ; save starting addr
; compute line to line offset
mov esi, [ebp].tb_image ; esi-> source image
lodsw ; get x width
mov bx,ax
lodsw
mov [ebp].tb_height,ax
add [ebp].tb_image,4
mov dx, bx ; save copy in dx
shr bx, 2 ; /4 = width in bands
mov ax, screen_width ; get screen width
sub ax, bx ; - (bitmap width/4)
mov [ebp].tb_lineo, ax ; save line width offset
mov [ebp].tb_pixcount, bx ; minimum # pix to copy
and dx, plane_bits ; get "partial band" size (0-3)
mov [ebp].tb_pixskew, dx ; also end of line skew
mov [ebp].tb_skewflag, dx ; save as flag/count
and cx, plane_bits ; cl = starting plane #
mov ax, map_mask_plane2 ; plane mask & plane select
shl ah, cl ; select correct plane
out_16 sc_index, ax ; select plane...
mov bh, ah ; bh = saved plane mask
mov bl, 4 ; bl = planes to copy
@tb_copy_plane:
mov esi, [ebp].tb_image ; esi-> source image
mov dx, [ebp].tb_height ; # of lines to copy
mov edi, [ebp].tb_start ; edi-> dest pos
; here ah is set with the value to be considered
; "transparent". it can be changed!
@tb_copy_line:
mov ah, 0 ; value to detect 0
mov cx, [ebp].tb_pixcount ; min # to copy
test cl, 0fch ; 16+pixwide?
jz @tb_copy_remainder ; nope...
; pixel copy loop has been unrolled to x4
@tb_copy_loop:
lodsb ; get pixel value in al
add esi, 3 ; skip to next byte in same plane
cmp al, ah ; it is "transparent"?
je @tb_skip_01 ; skip ahead if so
mov [edi], al ; copy pixel to vga screen
@tb_skip_01:
lodsb ; get pixel value in al
add esi, 3 ; skip to next byte in same plane
cmp al, ah ; it is "transparent"?
je @tb_skip_02 ; skip ahead if so
mov [edi+1], al ; copy pixel to vga screen
@tb_skip_02:
lodsb ; get pixel value in al
add esi, 3 ; skip to next byte in same plane
cmp al, ah ; it is "transparent"?
je @tb_skip_03 ; skip ahead if so
mov [edi+2], al ; copy pixel to vga screen
@tb_skip_03:
lodsb ; get pixel value in al
add esi, 3 ; skip to next byte in same plane
cmp al, ah ; it is "transparent"?
je @tb_skip_04 ; skip ahead if so
mov [edi+3], al ; copy pixel to vga screen
@tb_skip_04:
add edi, 4 ; adjust pixel write location
sub cl, 4 ; pixels to copy=-4
test cl, 0fch ; 4+ pixels left?
jnz @tb_copy_loop ; if so, do another block
@tb_copy_remainder:
jcxz @tb_next_line ; any pixels left on line
@tb_copy2:
lodsb ; get pixel value in al
add esi, 3 ; skip to next byte in same plane
cmp al, ah ; it is "transparent"?
je @tb_skip_05 ; skip ahead if so
mov [edi], al ; copy pixel to vga screen
@tb_skip_05:
inc edi ; advance dest addr
loopx cx, @tb_copy2 ; pixels to copy--, loop until done
@tb_next_line:
; any partial pixels? (some planes only)
or cx, [ebp].tb_skewflag ; get skew count
jz @tb_next2 ; if no partial pixels
lodsb ; get pixel value in al
dec esi ; backup to align
cmp al, ah ; it is "transparent"?
je @tb_next2 ; skip ahead if so
mov [edi], al ; copy pixel to vga screen
@tb_next2:
movzx eax, [ebp].tb_pixskew ; adjust skew
add esi, eax
movzx eax, [ebp].tb_lineo ; set to next display line
add edi, eax
loopx dx, @tb_copy_line ; lines to copy--, loop if more
;copy next plane....
dec bl ; planes to go--
jz @tb_exit ; hey! we are done
rol bh, 1 ; next plane in line...
out_8 sc_data, bh ; select plane
cmp al, 12h ; carry set if al=11h
adc [ebp].tb_start, 0 ; screen addr =+carry
inc w [ebp].tb_image ; start @ next byte
sub [ebp].tb_skewflag, 1 ; reduce planes to skew
adc [ebp].tb_skewflag, 0 ; back to 0 if it was -1
jmp @tb_copy_plane ; go copy the next plane
@tb_exit:
add esp, 14 ; deallocate workspace
pop edi esi ebp ; restore saved registers
ret 8 ; exit and clean up stack
; ==== video memory to video memory copy routines =====
;==================================
;copy_page (sourcepage%, destpage%)
;==================================
;
; duplicate on display page onto another
;
; entry: sourcepage = display page # to duplicate
; destpage = display page # to hold copy
;
; exit: no meaningful values returned
;
cp_stack struc
dd ?x3 ; edi, esi, ebp
dd ? ; caller
cp_destp dw ? ; page to hold copied image
cp_sourcep dw ? ; page to make copy from
cp_stack ends
public copy_page
copy_page:
push ebp esi edi ; preserve important registers
mov ebp, esp ; set up stack frame
cld ; block xfer forwards
; make sure page #'s are valid
mov ax, [ebp].cp_sourcep ; get source page #
cmp ax, last_page ; is it > max page #?
jae @cp_exit ; if so, abort
mov bx, [ebp].cp_destp ; get destination page #
cmp bx, last_page ; is it > max page #?
jae @cp_exit ; if so, abort
cmp ax, bx ; pages #'s the same?
je @cp_exit ; if so, abort
; setup esi and edi to video pages
shl bx, 2 ; scale index to dword
mov edi, page_addr[ebx] ; offset to dest page
mov bx, ax ; index to source page
shl bx, 2 ; scale index to dword
mov esi, page_addr[ebx] ; offset to source page
movzx ecx, page_size ; get size of page
; setup vga registers for mem to mem copy
out_16 gc_index, latches_on ; data from latches = on
out_16 sc_index, all_planes_on ; copy all planes
; note.. do *not* use movsw or movsd - they will
; screw with the latches which are 8 bits x 4
rep movsb ; copy entire page!
; reset vga for normal memory access
out_16 gc_index, latches_off ; data from latches = off
@cp_exit:
pop edi esi ebp ; restore saved registers
ret 4 ; exit and clean up stack
;==========================================================================
;copy_bitmap (sourcepage%, x1%, y1%, x2%, y2%, destpage%, destx1%, desty1%)
;==========================================================================
;
; copies a bitmap image from one display page to another
; this routine is limited to copying images with the same
; plane alignment. to work: (x1 mod 4) must = (destx1 mod 4)
; copying an image to the same page is supported, but results
; may be defined when the when the rectangular areas
; (x1, y1) - (x2, y2) and (destx1, desty1) -
; (destx1+(x2-x1), desty1+(y2-y1)) overlap...
; no paramter checking to done to insure that
; x2 >= x1 and y2 >= y1. be careful...
;
; entry: sourcepage = display page # with source image
; x1 = upper left xpos of source image
; y1 = upper left ypos of source image
; x2 = lower right xpos of source image
; y2 = lower right ypos of source image
; destpage = display page # to copy image to
; destx1 = xpos to copy ul corner of image to
; desty1 = ypos to copy ul corner of image to
;
; exit: ax = success flag: 0 = failure / -1= success
;
cb_stack struc
cb_height dw ? ; height of image in lines
cb_width dw ? ; width of image in "bands"
dd ?x3 ; edi, esi, ebp
dd ? ; caller
cb_desty1 dw ? ; destination ypos
cb_destx1 dw ? ; destination xpos
cb_destp dw ? ; page to copy bitmap to
cb_y2 dw ? ; lr ypos of image
cb_x2 dw ? ; lr xpos of image
cb_y1 dw ? ; ul ypos of image
cb_x1 dw ? ; ul xpos of image
cb_sourcep dw ? ; page containing source bitmap
cb_stack ends
public copy_bitmap
copy_bitmap:
push ebp esi edi ; preserve important registers
sub esp, 4 ; allocate workspace on stack
mov ebp, esp ; set up stack frame
; prep registers (and keep jumps short!)
cld ; block xfer forwards
; make sure parameters are valid
movzx ebx, [ebp].cb_sourcep ; get source page #
cmp bx, last_page ; is it > max page #?
jae @cb_abort ; if so, abort
mov cx, [ebp].cb_destp ; get destination page #
cmp cx, last_page ; is it > max page #?
jae @cb_abort ; if so, abort
mov ax, [ebp].cb_x1 ; get source x1
xor ax, [ebp].cb_destx1 ; compare bits 0-1
and ax, plane_bits ; check plane bits
jnz @cb_abort ; they should cancel out
; setup for copy processing
out_8 sc_index, map_mask ; set up for plane select
out_16 gc_index, latches_on ; data from latches = on
; compute info about images, setup esi & edi
mov ax, [ebp].cb_y2 ; height of bitmap in lines
sub ax, [ebp].cb_y1 ; is y2 - y1 + 1
inc ax ; (add 1 since were not 0 based)
mov [ebp].cb_height, ax ; save on stack for later use
mov ax, [ebp].cb_x2 ; get # of "bands" of 4 pixels
mov dx, [ebp].cb_x1 ; the bitmap occupies as x2-x1
shr ax, 2 ; get x2 band (x2 / 4)
shr dx, 2 ; get x1 band (x1 / 4)
sub ax, dx ; ax = # of bands - 1
inc ax ; ax = # of bands
mov [ebp].cb_width, ax ; save on stack for later use
shl bx, 2 ; scale source page to dword
mov esi, page_addr[ebx] ; si = offset of source page
mov ax, [ebp].cb_y1 ; get source y1 line
mul screen_width ; ax = offset to line y1
movzx eax, ax
add esi, eax ; si = offset to line y1
mov ax, [ebp].cb_x1 ; get source x1
shr ax, 2 ; x1 / 4 = byte offset
add esi, eax ; si = byte offset to (x1,y1)
mov bx, cx ; dest page index to bx
shl bx, 2 ; scale source page to dword
mov edi, page_addr[ebx] ; di = offset of dest page
mov ax, [ebp].cb_desty1 ; get dest y1 line
mul screen_width ; ax = offset to line y1
movzx eax, ax
add edi, eax ; di = offset to line y1
mov ax, [ebp].cb_destx1 ; get dest x1
shr ax, 2 ; x1 / 4 = byte offset
add edi, eax ; di = byte offset to (d-x1,d-y1)
mov cx, [ebp].cb_width ; cx = width of image (bands)
dec cx ; cx = 1?
je @cb_only_one_band ; 0 means image width of 1 band
mov bx, [ebp].cb_x1 ; get source x1
and bx, plane_bits ; aligned? (bits 0-1 = 00?)
jz @cb_check_right ; if so, check right alignment
jnz @cb_left_band ; not aligned? well..
@cb_abort:
clr ax ; return false (failure)
jmp @cb_exit ; and finish up
; copy when left & right clip masks overlap...
@cb_only_one_band:
mov bx, [ebp].cb_x1 ; get left clip mask
and bx, plane_bits ; mask out row #
mov al, left_clip_mask[ebx] ; get left edge mask
mov bx, [ebp].cb_x2 ; get right clip mask
and bx, plane_bits ; mask out row #
and al, right_clip_mask[ebx] ; get right edge mask byte
out_8 sc_data, al ; clip for left & right masks
mov cx, [ebp].cb_height ; cx = # of lines to copy
movzx edx, screen_width ; dx = width of screen
clr ebx ; bx = offset into image
@cb_one_loop:
mov al, [esi+ebx] ; load latches
mov [edi+ebx], al ; unload latches
add bx, dx ; advance offset to next line
loopjz cx, @cb_one_done ; exit loop if finished
mov al, [esi+ebx] ; load latches
mov [edi+ebx], al ; unload latches
add bx, dx ; advance offset to next line
loopx cx, @cb_one_loop ; loop until finished
@cb_one_done:
jmp @cb_finish ; outa here!
; copy left edge of bitmap
@cb_left_band:
out_8 sc_data, left_clip_mask[ebx] ; set left edge plane mask
mov cx, [ebp].cb_height ; cx = # of lines to copy
mov dx, screen_width ; dx = width of screen
clr ebx ; bx = offset into image
@cb_left_loop:
mov al, [esi+ebx] ; load latches
mov [edi+ebx], al ; unload latches
add bx, dx ; advance offset to next line
loopjz cx, @cb_left_done ; exit loop if finished
mov al, [esi+ebx] ; load latches
mov [edi+ebx], al ; unload latches
add bx, dx ; advance offset to next line
loopx cx, @cb_left_loop ; loop until finished
@cb_left_done:
inc edi ; move dest over 1 band
inc esi ; move source over 1 band
dec [ebp].cb_width ; band width--
; determine if right edge of bitmap needs special copy
@cb_check_right:
mov bx, [ebp].cb_x2 ; get source x2
and bx, plane_bits ; aligned? (bits 0-1 = 11?)
cmp bl, 03h ; plane = 3?
je @cb_copy_middle ; copy the middle then!
; copy right edge of bitmap
@cb_right_band:
out_8 sc_data, right_clip_mask[ebx] ; set right edge plane mask
dec [ebp].cb_width ; band width--
mov cx, [ebp].cb_height ; cx = # of lines to copy
mov dx, screen_width ; dx = width of screen
movzx ebx, [ebp].cb_width ; bx = offset to right edge
@cb_right_loop:
mov al, [esi+ebx] ; load latches
mov [edi+ebx], al ; unload latches
add bx, dx ; advance offset to next line
loopjz cx, @cb_right_done ; exit loop if finished
mov al, [esi+ebx] ; load latches
mov [edi+ebx], al ; unload latches
add bx, dx ; advance offset to next line
loopx cx, @cb_right_loop ; loop until finished
@cb_right_done:
; copy the main block of the bitmap
@cb_copy_middle:
mov cx, [ebp].cb_width ; get width remaining
jcxz @cb_finish ; exit if done
out_8 sc_data, all_planes ; copy all planes
mov dx, screen_width ; get width of screen minus
sub dx, cx ; image width (for adjustment)
movzx edx, dx
mov ax, [ebp].cb_height ; ax = # of lines to copy
movzx ecx,cx
mov ebx, ecx ; bx = quick rep reload count
; actual copy loop. rep movsb does the work
@cb_middle_copy:
mov ecx, ebx ; recharge rep count
rep movsb ; move bands
loopjz ax, @cb_finish ; exit loop if finished
add esi, edx ; adjust esi to next line
add edi, edx ; adjust edi to next line
mov ecx, ebx ; recharge rep count
rep movsb ; move bands
add esi, edx ; adjust esi to next line
add edi, edx ; adjust edi to next line
loopx ax, @cb_middle_copy ; copy lines until done
@cb_finish:
out_16 gc_index, latches_off ; data from latches = on
@cb_exit:
add esp, 4 ; deallocate stack workspace
pop edi esi ebp ; restore saved registers
ret 16 ; exit and clean up stack
; return to mode 03 before exiting to dos
public mode03
mode03:
mov v86r_ax,3h
mov al,10h
int 33h
ret
fb_stack struc
fb_add dw ?
fb_count dd ?
dd ?x3 ; edi, esi, ebp
dd ? ; caller
fb_pal dd ? ; source palette
fb_stack ends
public wipeoffpalette
public fadeoffpalette
public fadeonpalette
; load esi to palette
tmppal db 768 dup (0)
wipeoffpalette:
xor al,al ; wipe palette, clear all to r0,g0,b0
mov dx,3c8h
out dx,al
inc dx
mov cx,768
xor al,al
wipeit: out dx,al
loop wipeit
ret
fadeoffpalette:
mov bh, 2 ; bh = step
mov ah,0 ; ah = starting subtact
jmp fadepalette
fadeonpalette:
mov bh,-2
mov ah,64
fadepalette:
push ebp esi edi ; preserve important registers
sub esp, 6
mov ebp, esp ; set up stack frame
mov ecx,32
cld ; block xfer forwards
fonp2:
mov esi,[ebp].fb_pal
mov [ebp].fb_add,ax
mov [ebp].fb_count,ecx
mov edi,offset tmppal
mov ecx,768
fonp1:
lodsb
sub al,ah
jnc fonp3
xor al,al
fonp3:
stosb
loop fonp1
call sync_display
mov dx,3c8h
mov esi,offset tmppal
mov ecx,768/6
xor al,al
out dx,al
inc dx
fonp4:
lodsb
out dx,al
lodsb
out dx,al
lodsb
out dx,al
lodsb
out dx,al
lodsb
out dx,al
lodsb
out dx,al
loop fonp4
call sync_display
mov ecx,[ebp].fb_count
mov ax,[ebp].fb_add
add ah,bh
loop fonp2
add esp,6
pop edi esi ebp
ret 4
public turn_screen_off
public turn_screen_on
turn_screen_off: ; guess what these do!
mov dx,03dah ; these are used when changing video modes
in al,dx ; to avoid any flicker
mov dx,03c0h
mov al,0
out dx,al
ret
turn_screen_on:
mov dx,03dah
in al,dx
mov dx,03c0h
mov al,20h
out dx,al
ret
code32 ends
end